/*
 * Brush.java
 *
 * Created on December 30, 2007, 7:54 PM
 */

package picto.core;

import java.awt.*;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.*;
import java.util.HashMap;
import org.w3c.dom.Element;
import java.util.Map;
import org.w3c.dom.Document;
import picto.io.Archivable;
import picto.io.ArchiveException;
import picto.io.ArchiveReader;
import picto.io.ArchiveWriter;

/**
 * Load brush color:
 *         setTextureColor(Color)
 *         getTextureColor()
 *         getModifiedTexture()
 *      on setTexture grab the pixels
 *      on setColorTexture change the color of the pixels
 *          use a dirty flag for getModifiedTexture
 *              iterate through pixels and shift the RGB, leaving alpha
 *
 *
 
MemoryImageSource source;
SampleModel sample;
DataBufferInt data;
DirectColorModel colormodel;
WritableRaster raster;
BufferedImage buffer;
int[] pixels;
 
// Set up our pixel array
int w = 255;
int h = 255;
pixels = new int[w * h];
 
// This creates an RGBA colormodel... I think.
colormodel = new DirectColorModel(32, 0xff000000, 0xff0000, 0xff00, 0xff);
 
// Fill in some data. It doesn't really matter what.
int i = 0;
for(int y = 0; y < h; y++)
{
	for(int x = 0; x < w; x++)
	{
		pixels[i++] = (x << 24) | (255 << 16) | (255 << 8) | 255;
	}
}
 
// Set up our MemoryImageSource with the RGBA colormodel and data
source = new MemoryImageSource(w, h, colormodel, pixels, 0, w);
 
// And then, some magical steps which I don't understand
sample = colormodel.createCompatibleSampleModel(w, h);
data = new DataBufferInt(pixels, w * h);
raster = WritableRaster.createWritableRaster(sample, data, new Point(0,0));
image = new BufferedImage(colormodel, raster, false, null);


 *
 *
 * @author davedes
 */
public class Brush implements Archivable {
    
    public static final int DEFAULT_DIAMETER = 5;
    
    private String name;
    private ArchivableImage texture;
    private int diameter = DEFAULT_DIAMETER;
    private boolean smudge = false;
    
    private boolean textureDirty = true;
    private boolean colorDirty = true;
    private Color modColor = Color.black;
    private BufferedImage modImg = null;
        
    private static HashMap<String, Brush> presets = new HashMap<String, Brush>();
    
    /** Creates a new instance of Brush */
    public Brush(String name, int diameter) {
        this.name = name;
        this.diameter = diameter;
    }
    
    public Brush(String name) {
        this.name = name;
    }
    
    public Brush() {
    }
    
    public static Brush putPreset(String key, Brush brush) {
        return presets.put(key, brush);
    }
    
    public static Brush getPreset(String key) {
        return presets.get(key);
    }
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void ensureModifiedTexture() {
        if (texture==null||texture.getImage()==null)
            return;
        BufferedImage orig = texture.getImage();
        
        if (colorDirty) {
            colorDirty = false;
            int w=texture.getWidth(),h=texture.getHeight();
            if (textureDirty) {
                textureDirty = false;
                
                //create the image
                modImg = new BufferedImage(w, h, ImageState.DEFAULT_ARGB_PRE);
                modImg.setData(orig.getData());
            }
            
            int modrgb = modColor.getRGB();
            int r = (modrgb >> 16) & 0xFF;
            int g = (modrgb >> 8)  & 0xFF;
            int b = (modrgb >> 0)  & 0xFF;
            
            /*DataBuffer db = modImg.getData().getDataBuffer();
            if (db instanceof DataBufferInt) {
                int[] pixels = ((DataBufferInt)db).getData();
                
                int i = 0;
                for(int y = 0; y < h; y++) {
                    for(int x = 0; x < w; x++) {
                        int p = pixels[i];
                        int a = (p >> 24) & 0xFF;
                        pixels[i] = torgb(r, g, b, a);
                        i++;
                    }
                }
            }*/
            
            //TODO: use DataBuffer for direct access
            for (int y=0; y<h; y++) {
                for (int x=0; x<w; x++) {
                    int pixel = modImg.getRGB(x, y);

                    int a = (pixel >> 24) & 0xFF;
                    modImg.setRGB(x, y, torgb(r, g, b, a));
                }
            }
        }
    }
    
    private int torgb(int r, int g, int b, int a) {
        return ((a & 0xFF) << 24) |
                ((r & 0xFF) << 16) |
                ((g & 0xFF) << 8)  |
                ((b & 0xFF) << 0);
    }
    
    
    public Image getModifiedTexture() {
        ensureModifiedTexture();
        return modImg;
    }
        
    public void setTextureColor(Color c) {
        if (c!=null && !c.equals(modColor)) {
            colorDirty = true;
            modColor = c;
        }
    }
    
    public Color getTextureColor() {
        return modColor;
    }
    
    public ArchivableImage getTexture() {
        return texture;
    }

    public void setTexture(ArchivableImage texture) {
        if (this.texture!=texture)
            textureDirty = true;
        this.texture = texture;
    }

    public int getDiameter() {
        return diameter;
    }

    public void setDiameter(int diameter) {
        this.diameter = diameter;
    }

    public boolean isSmudge() {
        return smudge;
    }

    public void setSmudge(boolean smudge) {
        this.smudge = smudge;
    }
    
    public boolean equals(Object o) {
        if (o!=null && o instanceof Brush) {
            Brush b = (Brush)o;
            return this.diameter == b.diameter
                    && (this.name==null&&b.name==null || this.name.equals(b.name))
                    && this.smudge == b.smudge
                    && (this.texture==null&&b.texture==null || this.texture.equals(b.texture));
        } else
            return false;
    }
    
    public void read(ArchiveReader reader, Element root) {
        if (!"brush".equals(root.getNodeName()))
            throw new IllegalArgumentException("Brush must be parsed from <brush> tag");
        
        this.name = null;
        this.diameter = DEFAULT_DIAMETER;
        this.smudge = false;
        
        String preKey = root.getAttribute("preset");
        if (preKey.length()!=0) {
            Brush other = Brush.getPreset(preKey);
            this.name = other.name;
            this.texture = other.texture;
            this.diameter = other.diameter;
            this.smudge = other.smudge;
        }
        
        String nameStr = root.getAttribute("name");
        if (nameStr.length()!=0)
            this.name = nameStr;
                
        String diamStr = root.getAttribute("diameter");
        if (diamStr.length()!=0) {
            try { this.diameter = Integer.parseInt(diamStr); }
            catch (NumberFormatException e) { } 
        }
        
        String smudgeStr = root.getAttribute("smudge");
        if (smudgeStr.length()!=0)
            this.smudge = Boolean.parseBoolean(smudgeStr);
        
        try {
            CachedImage t2 = CachedImage.readCachedImage(reader, root, this.name);
            this.texture = t2;
        } catch (ArchiveException exc) {
            System.err.println("WARN: Cannot load texture data in brush "+this.name);
        }
    }
    
    public Element write(ArchiveWriter writer) throws ArchiveException {
        Document doc = writer.getDocument();
        Element root = doc.createElement("brush");
        
        String presetKey = null;
        for (Map.Entry<String, Brush> e : presets.entrySet()) {
            String key = e.getKey();
            Brush brush = e.getValue();
            if (this.equals(brush)) {
                presetKey = key;
                break;
            }
        }
        
        if (presetKey!=null) {
            root.setAttribute("preset", presetKey);
        } else {
            if (name!=null)
                root.setAttribute("name", name);
            root.setAttribute("diameter", Integer.toString(diameter));
            root.setAttribute("smudge", Boolean.toString(smudge));
            
            if (texture!=null) {
                root.appendChild(texture.write(writer));
            }
        }
        return root;
    }
}
